home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / c / nos042_s / mbuf.c < prev    next >
C/C++ Source or Header  |  1994-09-16  |  13KB  |  585 lines

  1. /* mbuf (message buffer) primitives
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. #include <stdio.h>
  5. #include <dos.h>    /* TEMP */
  6. #include "global.h"
  7. #include "mbuf.h"
  8. #include "proc.h"
  9.  
  10. /* Interrupt buffer pool */
  11. int Intqlen;            /* Number of free mbufs on Intq */
  12. struct mbuf *Intq;        /* Mbuf pool for interrupt handlers */
  13. struct mbuf *Garbq;        /* List of buffers freed at interrupt time */
  14. long Ibuffail;            /* Allocate failures */
  15.  
  16. #ifndef ATARI
  17. void
  18. refiq()
  19. {
  20.     register struct mbuf *bp;
  21.     char i_state;
  22.     int32 dma_abs;    /* TEMP */
  23.     int16 dma_page;    /* TEMP */
  24.  
  25.     /* Empty the garbage */
  26.     if(Garbq != NULLBUF){
  27.         i_state = dirps();
  28.         bp = Garbq;
  29.         Garbq = NULLBUF;
  30.         restore(i_state);
  31.         free_p(bp);
  32.     }
  33.     /* Replenish interrupt buffer pool */
  34.     while(Intqlen < Nibufs){
  35.         if((bp = alloc_mbuf(Ibufsize)) == NULLBUF)
  36.             break;
  37. #ifndef    notdef        /* Temp hack to satisfy PI DMA requirements */
  38.         dma_abs = ((long)FP_SEG(bp->data) << 4) + (long)FP_OFF(bp->data);
  39.         dma_page = dma_abs >> 16;
  40.         if(((dma_abs+Ibufsize) >> 16) != dma_page){
  41.             i_state = dirps();
  42.             bp->next = Garbq;
  43.             Garbq = bp;
  44.             restore(i_state);
  45.             continue;
  46.         }
  47. #endif
  48.  
  49.         i_state = dirps();
  50.         bp->next = Intq;
  51.         Intq = bp;
  52.         Intqlen++;
  53.         restore(i_state);
  54.  
  55.     }
  56. }
  57. #endif
  58.  
  59. void
  60. iqstat()
  61. {
  62.     tprintf("Intqlen %u Ibufsize %u Ibuffail %lu\n",
  63.         Intqlen,Ibufsize,Ibuffail);
  64. }
  65. /* Allocate mbuf with associated buffer of 'size' bytes. If interrupts
  66.  * are enabled, use the regular heap. If they're off, use the special
  67.  * interrupt buffer pool.
  68.  */
  69. struct mbuf *alloc_mbuf(int16 size)
  70. {
  71.     register struct mbuf *bp;
  72.  
  73.     if(istate()){
  74.         /* Interrupts are enabled, use the heap normally */
  75.         bp = (struct mbuf *)malloc((unsigned)(size + sizeof(struct mbuf)));
  76.         if(bp == NULLBUF)
  77.             return NULLBUF;
  78.         /* Clear just the header portion */
  79.         memset((char *)bp,0,sizeof(struct mbuf));
  80.         if((bp->size = size) != 0)
  81.             bp->data = (char *)(bp + 1);
  82.         bp->refcnt++;
  83.     } else {
  84.         /* Interrupts are off, use special interrupt buffer pool */
  85.         if(size > Ibufsize || Intq == NULLBUF){
  86.             Ibuffail++;
  87.             return NULLBUF;
  88.         }
  89.         bp = Intq;
  90.         Intq = bp->next;
  91.         bp->next = NULLBUF;
  92.         Intqlen--;
  93.     }
  94.     return bp;
  95. }
  96. /* Allocate mbuf, waiting if memory is unavailable */
  97. struct mbuf *ambufw(
  98.     int16 size)
  99. {
  100.     register struct mbuf *bp;
  101.  
  102.     bp = (struct mbuf *)mallocw((unsigned)(size + sizeof(struct mbuf)));
  103.  
  104.     /* Clear just the header portion */
  105.     memset((char *)bp,0,sizeof(struct mbuf));
  106.     if((bp->size = size) != 0)
  107.         bp->data = (char *)(bp + 1);
  108.     bp->refcnt++;
  109.     return bp;
  110. }
  111.  
  112. /* Decrement the reference pointer in an mbuf. If it goes to zero,
  113.  * free all resources associated with mbuf.
  114.  * Return pointer to next mbuf in packet chain
  115.  */
  116. struct mbuf *
  117. free_mbuf(bp)
  118. register struct mbuf *bp;
  119. {
  120.     struct mbuf *bpnext;
  121.  
  122.     if(bp == NULLBUF)
  123.         return NULLBUF;
  124.  
  125.     bpnext = bp->next;
  126.     if(bp->dup != NULLBUF){
  127.         free_mbuf(bp->dup);    /* Follow indirection */
  128.         bp->dup = NULLBUF;
  129.     }
  130.     /* Decrement reference count. If it has gone to zero, free it. */
  131.     if(--bp->refcnt <= 0){
  132.         if(istate()){
  133.             free((char *)bp);
  134.         } else {
  135.             /* If the interrupt pool isn't full and this buffer
  136.              * appears to have come from it, put it back.
  137.              * Otherwise put it on the garbage list where it
  138.              * will be freed by refiq() later with interrupts
  139.              * enabled.
  140.              *
  141.              * This test handles the common special case of
  142.              * an interrupt handler allocating a buffer and
  143.              * then freeing it before returning (e.g., due to
  144.              * a receive abort or CRC failure).
  145.              */
  146.             bp->refcnt = 1;    /* Adjust */
  147.             if(bp->size == Ibufsize && Intqlen < Nibufs){
  148.                 bp->next = Intq;
  149.                 bp->anext = NULLBUF;
  150.                 bp->data = (char *)(bp + 1);
  151.                 bp->cnt = 0;
  152.                 Intq = bp;
  153.                 Intqlen++;
  154.             } else {
  155.                 bp->next = Garbq;
  156.                 Garbq = bp;
  157.             }
  158.         }
  159.     }
  160.     return bpnext;
  161. }
  162.  
  163. /* Free packet (a chain of mbufs). Return pointer to next packet on queue,
  164.  * if any
  165.  */
  166. struct mbuf *
  167. free_p(bp)
  168. register struct mbuf *bp;
  169. {
  170.     register struct mbuf *abp;
  171.  
  172.     if(bp == NULLBUF)
  173.         return NULLBUF;
  174.     abp = bp->anext;
  175.     while(bp != NULLBUF)
  176.         bp = free_mbuf(bp);
  177.     return abp;
  178. }        
  179. /* Free entire queue of packets (of mbufs) */
  180. void
  181. free_q(q)
  182. struct mbuf **q;
  183. {
  184.     register struct mbuf *bp;
  185.  
  186.     while((bp = dequeue(q)) != NULLBUF)
  187.         free_p(bp);
  188. }
  189.  
  190. /* Count up the total number of bytes in a packet */
  191. int16
  192. len_p(bp)
  193. register struct mbuf *bp;
  194. {
  195.     register int16 cnt = 0;
  196.  
  197.     while(bp != NULLBUF){
  198.         cnt += bp->cnt;
  199.         bp = bp->next;
  200.     }
  201.     return cnt;
  202. }
  203. /* Count up the number of packets in a queue */
  204. int16
  205. len_q(bp)
  206. register struct mbuf *bp;
  207. {
  208.     register int16 cnt;
  209.  
  210.     for(cnt=0;bp != NULLBUF;cnt++,bp = bp->anext)
  211.         ;
  212.     return cnt;
  213. }
  214. /* Trim mbuf to specified length by lopping off end */
  215. void trim_mbuf(
  216.     struct mbuf **bpp,
  217.     int16 length)
  218. {
  219.     register int16 tot = 0;
  220.     register struct mbuf *bp;
  221.  
  222.     if(bpp == NULLBUFP || *bpp == NULLBUF)
  223.         return;    /* Nothing to trim */
  224.  
  225.     if(length == 0){
  226.         /* Toss the whole thing */
  227.         free_p(*bpp);
  228.         *bpp = NULLBUF;
  229.         return;
  230.     }
  231.     /* Find the point at which to trim. If length is greater than
  232.      * the packet, we'll just fall through without doing anything
  233.      */
  234.     for( bp = *bpp; bp != NULLBUF; bp = bp->next){
  235.         if(tot + bp->cnt < length){
  236.             tot += bp->cnt;
  237.         } else {
  238.             /* Cut here */
  239.             bp->cnt = length - tot;
  240.             free_p(bp->next);
  241.             bp->next = NULLBUF;
  242.             break;
  243.         }
  244.     }
  245. }
  246. /* Duplicate/enqueue/dequeue operations based on mbufs */
  247.  
  248. /* Duplicate first 'cnt' bytes of packet starting at 'offset'.
  249.  * This is done without copying data; only the headers are duplicated,
  250.  * but without data segments of their own. The pointers are set up to
  251.  * share the data segments of the original copy. The return pointer is
  252.  * passed back through the first argument, and the return value is the
  253.  * number of bytes actually duplicated.
  254.  */
  255. int16 dup_p(
  256.     struct mbuf **hp,
  257.     struct mbuf *bp,
  258.     int16 offset,
  259.     int16 cnt)
  260. {
  261.     register struct mbuf *cp;
  262.     int16 tot;
  263.  
  264.     if(cnt == 0 || bp == NULLBUF || hp == NULLBUFP){
  265.         if(hp != NULLBUFP)
  266.             *hp = NULLBUF;
  267.         return 0;
  268.     }
  269.     if((*hp = cp = alloc_mbuf(0)) == NULLBUF){
  270.         return 0;
  271.     }
  272.     /* Skip over leading mbufs that are smaller than the offset */
  273.     while(bp != NULLBUF && bp->cnt <= offset){
  274.         offset -= bp->cnt;
  275.         bp = bp->next;
  276.     }
  277.     if(bp == NULLBUF){
  278.         free_mbuf(cp);
  279.         *hp = NULLBUF;
  280.         return 0;    /* Offset was too big */
  281.     }
  282.     tot = 0;
  283.     for(;;){
  284.         /* Make sure we get the original, "real" buffer (i.e. handle the
  285.          * case of duping a dupe)
  286.          */
  287.         if(bp->dup != NULLBUF)
  288.             cp->dup = bp->dup;
  289.         else
  290.             cp->dup = bp;
  291.  
  292.         /* Increment the duplicated buffer's reference count */
  293.         cp->dup->refcnt++;
  294.  
  295.         cp->data = bp->data + offset;
  296.         cp->cnt = min(cnt,bp->cnt - offset);
  297.         offset = 0;
  298.         cnt -= cp->cnt;
  299.         tot += cp->cnt;
  300.         bp = bp->next;
  301.         if(cnt == 0 || bp == NULLBUF || (cp->next = alloc_mbuf(0)) == NULLBUF)
  302.             break;
  303.         cp = cp->next;
  304.     }
  305.     return tot;
  306. }
  307. /* Copy first 'cnt' bytes of packet into a new, single mbuf */
  308. struct mbuf *copy_p(
  309.     struct mbuf *bp,
  310.     int16 cnt)
  311. {
  312.     register struct mbuf *cp;
  313.     register char *wp;
  314.     register int16 n;
  315.  
  316.     if(bp == NULLBUF || cnt == 0 || (cp = alloc_mbuf(cnt)) == NULLBUF)
  317.         return NULLBUF;
  318.     wp = cp->data;
  319.     while(cnt != 0 && bp != NULLBUF){
  320.         n = min(cnt,bp->cnt);
  321.         memcpy(wp,bp->data,n);
  322.         wp += n;
  323.         cp->cnt += n;
  324.         cnt -= n;
  325.         bp = bp->next;
  326.     }
  327.     return cp;
  328. }
  329. /* Copy and delete "cnt" bytes from beginning of packet. Return number of
  330.  * bytes actually pulled off
  331.  */
  332. int16 pullup(
  333.     struct mbuf **bph,
  334.     char *buf,
  335.     int16 cnt)
  336. {
  337.     register struct mbuf *bp;
  338.     int16 n,tot;
  339.  
  340.     tot = 0;
  341.     if(bph == NULLBUFP)
  342.         return 0;
  343.     while(cnt != 0 && (bp = *bph) != NULLBUF){
  344.         n = min(cnt,bp->cnt);
  345.         if(buf != NULLCHAR){
  346.             if(n == 1)    /* Common case optimization */
  347.                 *buf = *bp->data;
  348.             else if(n > 1)
  349.                 memcpy(buf,bp->data,n);
  350.             buf += n;
  351.         }
  352.         tot += n;
  353.         cnt -= n;
  354.         bp->data += n;
  355.         bp->cnt -= n;        
  356.         if(bp->cnt == 0){
  357.             /* If this is the last mbuf of a packet but there
  358.              * are others on the queue, return a pointer to
  359.              * the next on the queue. This allows pullups to
  360.              * to work on a packet queue
  361.              */
  362.             if(bp->next == NULLBUF && bp->anext != NULLBUF){
  363.                 *bph = bp->anext;
  364.                 free_mbuf(bp);
  365.             } else
  366.                 *bph = free_mbuf(bp);
  367.         }
  368.     }
  369.     return tot;
  370. }
  371. /* Append mbuf to end of mbuf chain */
  372. void
  373. append(bph,bp)
  374. struct mbuf **bph;
  375. struct mbuf *bp;
  376. {
  377.     register struct mbuf *p;
  378.  
  379.     if(bph == NULLBUFP || bp == NULLBUF)
  380.         return;
  381.     if(*bph == NULLBUF){
  382.         /* First one on chain */
  383.         *bph = bp;
  384.     } else {
  385.         for(p = *bph ; p->next != NULLBUF ; p = p->next)
  386.             ;
  387.         p->next = bp;
  388.     }
  389. }
  390. /* Insert specified amount of contiguous new space at the beginning of an
  391.  * mbuf chain. If enough space is available in the first mbuf, no new space
  392.  * is allocated. Otherwise a mbuf of the appropriate size is allocated and
  393.  * tacked on the front of the chain.
  394.  *
  395.  * This operation is the logical inverse of pullup(), hence the name.
  396.  */
  397. struct mbuf *pushdown(
  398.     struct mbuf *bp,
  399.     int16 size)
  400. {
  401.     register struct mbuf *nbp;
  402.  
  403.     /* Check that bp is real, that it hasn't been duplicated, and
  404.      * that it itself isn't a duplicate before checking to see if
  405.      * there's enough space at its front.
  406.      */
  407.     if(bp != NULLBUF && bp->refcnt == 1 && bp->dup == NULLBUF
  408.      && bp->data - (char *)(bp+1) >= size){
  409.         /* No need to alloc new mbuf, just adjust this one */
  410.         bp->data -= size;
  411.         bp->cnt += size;
  412.     } else {
  413.         nbp = ambufw(size);
  414.         nbp->next = bp;
  415.         nbp->cnt = size;
  416.         bp = nbp;
  417.     }
  418.     return bp;
  419. }
  420. /* Append packet to end of packet queue */
  421. void
  422. enqueue(q,bp)
  423. struct mbuf **q;
  424. struct mbuf *bp;
  425. {
  426.     register struct mbuf *p;
  427.     char i_state;
  428.  
  429.     if(q == NULLBUFP || bp == NULLBUF)
  430.         return;
  431.     i_state = dirps();
  432.     if(*q == NULLBUF){
  433.         /* List is empty, stick at front */
  434.         *q = bp;
  435.     } else {
  436.         for(p = *q ; p->anext != NULLBUF ; p = p->anext)
  437.             ;
  438.         p->anext = bp;
  439.     }
  440.     restore(i_state);
  441.     psignal(q,1);
  442. }
  443. /* Unlink a packet from the head of the queue */
  444. struct mbuf *
  445. dequeue(q)
  446. register struct mbuf **q;
  447. {
  448.     register struct mbuf *bp;
  449.     char i_state;
  450.  
  451.     if(q == NULLBUFP)
  452.         return NULLBUF;
  453.     i_state = dirps();
  454.     if((bp = *q) != NULLBUF){
  455.         *q = bp->anext;
  456.         bp->anext = NULLBUF;
  457.     }
  458.     restore(i_state);
  459.     return bp;
  460. }    
  461.  
  462. /* Copy user data into an mbuf */
  463. struct mbuf *qdata(
  464.     char *data,
  465.     int16 cnt)
  466. {
  467.     register struct mbuf *bp;
  468.  
  469.     bp = ambufw(cnt);
  470.     memcpy(bp->data,data,cnt);
  471.     bp->cnt = cnt;
  472.     return bp;
  473. }
  474. /* Copy mbuf data into user buffer */
  475. int16
  476. dqdata(bp,buf,cnt)
  477. struct mbuf *bp;
  478. char *buf;
  479. unsigned cnt;
  480. {
  481.     int16 tot;
  482.     unsigned n;
  483.     struct mbuf *bp1;
  484.  
  485.     if(buf == NULLCHAR)
  486.         return 0;
  487.     
  488.     tot = 0;
  489.     for(bp1 = bp;bp1 != NULLBUF; bp1 = bp1->next){
  490.         n = min(bp1->cnt,cnt);
  491.         memcpy(buf,bp1->data,n);
  492.         cnt -= n;
  493.         buf += n;
  494.         tot += n;
  495.     }
  496.     free_p(bp);
  497.     return tot;
  498. }
  499. /* Pull a 32-bit integer in host order from buffer in network byte order.
  500.  * On error, return 0. Note that this is indistinguishable from a normal
  501.  * return.
  502.  */
  503. int32
  504. pull32(bpp)
  505. struct mbuf **bpp;
  506. {
  507.     char buf[4];
  508.  
  509.     if(pullup(bpp,buf,4) != 4){
  510.         /* Return zero if insufficient buffer */
  511.         return 0;
  512.     }
  513.     return get32(buf);
  514. }
  515. /* Pull a 16-bit integer in host order from buffer in network byte order.
  516.  * Return -1 on error
  517.  */
  518. long
  519. pull16(bpp)
  520. struct mbuf **bpp;
  521. {
  522.     char buf[2];
  523.  
  524.     if(pullup(bpp,buf,2) != 2){
  525.         return -1;        /* Nothing left */
  526.     }
  527.     return get16(buf);
  528. }
  529. /* Pull single character from mbuf */
  530. int
  531. pullchar(bpp)
  532. struct mbuf **bpp;
  533. {
  534.     char c;
  535.  
  536.     if(pullup(bpp,&c,1) != 1)
  537.         return -1;        /* Nothing left */
  538.     return (int)uchar(c);
  539. }
  540. int
  541. write_p(fp,bp)
  542. FILE *fp;
  543. struct mbuf *bp;
  544. {
  545.     while(bp != NULLBUF){
  546.         if(fwrite(bp->data,1,bp->cnt,fp) != bp->cnt)
  547.             return -1;
  548.         bp = bp->next;
  549.     }
  550.     return 0;
  551. }
  552. /* Reclaim unused space in a mbuf chain. If the argument is a chain of mbufs
  553.  * and/or it appears to have wasted space, copy it to a single new mbuf and
  554.  * free the old mbuf(s). But refuse to move mbufs that merely
  555.  * reference other mbufs, or that have other headers referencing them.
  556.  *
  557.  * Be extremely careful that there aren't any other pointers to
  558.  * (or into) this mbuf, since we have no way of detecting them here.
  559.  * This function is meant to be called only when free memory is in
  560.  * short supply.
  561.  */
  562. void
  563. mbuf_crunch(bpp)
  564. struct mbuf **bpp;
  565. {
  566.     register struct mbuf *bp = *bpp;
  567.     struct mbuf *nbp;
  568.  
  569.     if(bp->refcnt > 1 || bp->dup != NULLBUF){
  570.         /* Can't crunch, there are other refs */
  571.         return;
  572.     }
  573.     if(bp->next == NULLBUF && bp->cnt == bp->size){
  574.         /* Nothing to be gained by crunching */
  575.         return;
  576.     }
  577.     if((nbp = copy_p(bp,len_p(bp))) == NULLBUF){
  578.         /* Copy failed due to lack of (contiguous) space */
  579.         return;
  580.     }
  581.     nbp->anext = bp->anext;
  582.     free_p(bp);
  583.     *bpp = nbp;
  584. }
  585.